///<reference path="Start.ts"/>
///<reference path="Functions.ts"/>
class Server{
    constructor(){
        Server.init();
    }
    static init=function () {
        if(!inited)
            Server.watcher();
        inited=true;
    }

    static watcher=function () {
        var chokidar = require('chokidar');
        chokidar.watch([
            Config.APP_PATH+'/app/*.js',
            Config.APP_PATH+'/Db/*.js',
            Config.APP_PATH+'/Conf/*.js',
        ])
            .on('add',Server.clean_cache)
            .on('change',Server.clean_cache)
            .on('unlink',Server.clean_cache)
    }
    static clean_cache=function (path) {
        if(require.cache[path]){
            console.log("File Changed:"+path+" Reloaded")
            delete require.cache[path];
        }
    }
    static request=function (req,rep) {
        if(_.isUndefined(req.from)){
            req.from="HTTP";
        }

        req._config={};
        req.config=function (key=null,$default=null) {
            let p = req._config;
            if(key===null){
                return p
            }
            let o = key.split('.');
            o.forEach(v=>{
                if(_.isObject(p)&&!_.isUndefined(p)&&!_.isUndefined(p[v])){
                    p=p[v];
                }else{
                    p=$default;
                }
            })
            return p;
        }

        // 读取host并加载对应的Config文件
        // let host = req.headers.host;
        Server.config(req,rep);
        // 检测是否是静态文件，如果是则直接读取并输出
        // if()
        if(_.isString(req.body)&&'{'==req.body.substr(0,1)&&'}'==req.body.substr(req.body.length-1)){
            try{
                var body = JSON.parse(req.body);
                if(_.isObject(body)){
                    req.body=body;
                }
            }catch (e){

            }
        }
        let match = req.originalUrl.match(/\/([A-Za-z][A-Za-z0-9]{0,})\/([A-Za-z][A-Za-z0-9]{0,})/)
        let C,A;
        if(req.query.i!==undefined&&req.params.C===undefined){
            let i = req.query.i!==undefined?req.query.i.split('/'):['Error','_404']
            if(i.length==2){
                C = i[0];
                A = i[1];
            }else{
                C = "Error";
                A = "_404";
            }
        }else if(match.length==3){
            C=match[1];
            A=match[2];
        }else{
            C= req.params.C;
            A= req.params.A;
        }
        req.query.i=`${C}/${A}`;
        req.params.C=C;
        req.params.A=A;
        req.post=function (key,$default=null) {
            let o = key.split('.');
            let p = req.body;
            o.forEach(v=>{
                if(_.isObject(p)&&!_.isUndefined(p)&&!_.isUndefined(p[v])){
                    p=p[v];
                }else{
                    p=$default;
                }
            })
            return p;
        }
        req.request=function (key=null,$default=null) {
            var getp = function(k,d){
                let p = _.assign(req.query,req.body||{});
                if(key===null){
                    return p
                }
                let o = k.split('.');
                o.forEach(v=>{
                    if(_.isObject(p)&&!_.isUndefined(p)&&!_.isUndefined(p[v])){
                        p=p[v];
                    }else{
                        p=$default;
                    }
                })
                return p;
            }
            if(_.isString(key)){
                return getp(key,$default);
            }else if(_.isObject(key)){
                var Obj = {};
                _.forOwn(key,(v,k)=>{
                    Obj[k]=getp(k,v);
                })
                return Obj;
            }else if(_.isArray(key)){
                var Obj = {};
                _.forOwn(key,(v,k)=>{
                    Obj[v]=getp(v,$default);
                })
                return Obj;
            }else{
                return $default;
            }
        }

    }
    static run=function () {
        Server.init();


//加载session库
        const session = require('express-session');
// var cookieParser = require('cookie-parser');
//var Config = require(global.APP_PATH+'/config.js');
        var session_store;
        var session_path = Config.SESSION_PATH?`${Config.SESSION_PATH}/${Config.APP_NAME}`:(`/tmp/${Config.APP_NAME}`);
        switch (Config.SESSION_HANDLER.toLowerCase()){
            case "file":
                session_store = new (require('session-file-store')(session))({
                    path:session_path
                })
                break;
            case "redis":
                session_store = new (require('connect-redis')(session))(Config.SESSION_REDIS)
                break;
            default:
                session_store = new (require('session-file-store')(session))
                // console.error('不支持的session方式')
                break;
        }
        app.use(session({
                secret: 'keyboard cat',
                saveUninitialized:true,
                resave:false,
                cookie: { maxAge: 60*60*1000 },
                store:session_store
            }
        ))

        port = (process.env.VCAP_APP_PORT || process.env.PORT || Config.APP_PORT);
        host = (process.env.VCAP_APP_HOST || process.env.HOST || Config.APP_HOST);
        app.use(express.static(Config.PUBLIC_PATH,{
            index:['index.html','index.htm']
        }))
        console.log('Attach Static Path:'+Config.PUBLIC_PATH)
        var numCPUs=require('os').cpus().lengthAdjust,num=0;
        if(Config&&Config.MAX_PROCESS){
            num = Config.MAX_PROCESS>numCPUs?numCPUs:Config.MAX_PROCESS;
        }
        if(num>0){
            if (cluster.isMaster) {
                // Fork workers
                console.log(`${Config.APP_NAME} is Starting..Please Waiting.`)
                for (var i = 0; i < num; i++) {
                    cluster.fork();
                }
                cluster.on('exit', function(worker, code, signal) {
                    console.log('worker ' + worker.process.pid + ' restart');
                    setTimeout(function() {cluster.fork();},2000);
                });
            } else {
                Server.Start();
            }
        }else{
            Server.Start();
        }
    }

    /**
     * 加载配置文件
     * @param host
     * @constructor
     */
    static config=function (req,rep) {
        let host = req.headers.host;
        let ConfigPath = `${Config.APP_PATH}/Conf`;
        let Configs=[
            `${ConfigPath}/config.js`,
        ];
        if(Config.APP_DEBUG==true)
            Configs.push(`${ConfigPath}/debug.js`)
        Configs.push(`${ConfigPath}/${host}.js`)
        Configs.forEach(path=>{
            if(fs.existsSync(path)){
                try{
                    var config = require(path)
                    if(_.isObject(config)){
                        req._config=Object.assign(req._config,config);
                    }
                }catch (e){
                    debug(e);
                }finally {

                }
            }
        })
    }
    static controller(req,rep,next){
        let C,A;
        C=req.params.C;
        A=req.params.A;
        let Controllers = require(`${Config.APP_PATH}/app/Controller.js`);
        if(Controllers[C] instanceof Function){

        }else if(Controllers[C+'Controller'] instanceof Function){
            C=C+'Controller';
        }else
        {
            C='Error';
            A='_404';
        }
        try{
            let controller = new Controllers[C](req,rep);
            if(controller['_before_'+A] instanceof Function){
                controller['_before_'+A].apply(controller,[req.body])
                return ;
            }
            if(controller[A] instanceof Function){
                Q(controller[A].apply(controller,[req.body])).then(d=>{
                    if(!_.isUndefined(d)&&!_.isFunction(d))
                        controller.success(d)
                }).catch(e=>{
                    controller.error(e)
                })
                // let p =controller[A].apply(controller,[req.body])
                // if(!!p&&typeof p.then=="function"){
                //     p.then(d=>{
                //         controller.success(d)
                //     }).catch(e=>{
                //         controller.error(e)
                //     })
                // }
            }else{
                Server.NotFound(req,rep,next)
            }
            if(controller['_after_'+A] instanceof Function){
                controller['_after_'+A].apply(controller,[req.body])
                return ;
            }
        }catch (e){
            Server.Error(e,req,rep,next)
            next();
        }
    }
    /**
     * 处理请求的方法
     * @param req
     * @param rep
     * @param next
     */
    static onRequest=function (req,rep,next) {
        if(req.method=="OPTIONS"){
            rep.send("")
            next()
            return;
        }
        Server.request(req,rep);
        Server.controller(req,rep,next);
    }
    static ProxyPass(req,rep){
        request.post(Config.PROXY_PASS+req.url, {form:Config.PROXY_ENCODE(req,rep)},(err,response,body)=>{
            let c = new Controller(req,rep);
            rep.send(Config[`${req.from}_ENCODE`].apply(c,[Config.PROXY_DECODE(response,body)]));
        });
    }
    static NotFound(req,rep,next){
        if(Config.PROXY_PASS!==false){
            this.ProxyPass(req,rep);
        }else{

        }
    }
    /**
     * WebSocket监听方法
     * @constructor
     */
    static WebSocket=function () {
        io.on("authorization",function (hand,fn) {
            let parseCookie = require('cookie-parser');
            hand.cookie = parseCookie(hand.headers.cookie)
            let cid = hand.cookie['connect.sid']
            if(cid){
                session_store.get(cid,function (err,session) {
                    if(err){
                        fn(err.message,false)
                    }else{
                        hand.session=session
                        fn(null,true)
                    }
                })
            }else{
                fn('nosession',false)
            }
        })
        io.on('connection',function (socket) {
            console.log('new Connection')
            // var session = socket.handshake.session;
            socket.on('message',function () {
                
            })
            socket.on('login',function () {
                
            })
            socket.on('logout',function () {
                
            })
            socket.on('disconnect',function () {
                
            })
        })
    }
    static Redis=function (config={}) {
        let conf = Object.assign(Config.FROM_REDIS,config);
        var Redis = require('ioredis');
        var redis = new Redis(conf);
        var pub = new Redis(conf);
        redis.subscribe(conf.request,(err,count)=>{

        })
        redis.on('message',(channel,message)=>{
            console.log(`Redis From ${channel}: ${message}`);
            //此处解析成req和rep
            var body=Config.REDIS_DECODE(message,channel);
            var req={
                query:{
                    i:body.i
                },
                body:body.d,
                method:'redis',
                path:channel,
                baseUrl:channel,
                params:{},
                from:'REDIS'
            },rep={
                send:(data)=>{
                    pub.publish(conf.response,_.isString(data)?data:JSON.stringify(data));
                }
            };
            Server.onRequest(req,rep,()=>{})
        })
    }
    /**
     * 启动express
     * @constructor
     */
    static Start=function () {
        process.on('uncaughtException',function (err) {
            console.log("Error",err)
        })
        app.use(require('morgan')(Config.DEBUG?'dev':'short'))
        let multer = require('multer');
        let storage=multer.diskStorage({
            destination:function (req,file,cb) {
                var dir = [Config.TMP_PATH,"Upload",moment().format('YYYY_MM_DD_h_mm')].join(path.sep)
                mkdirs(dir)
                cb(null,dir)
            },filename:function (req,file,cb) {
                cb(null,[file.fieldname,file.originalname].join('_')+'.tmp')
            }
        })
        var upload = multer({storage});
        app.use(upload.any());
        app.all('/*',Server.onRequest)
        app.use(Server.Error)
        console.log('Process Start Success:'+host+":"+port);
        EM.emit('started',host,port)
        app.listen(port,host)
    }
    static Error=function (error,req,rep,next) {
        try{
            rep.send(Config[`${req.from}_ENCODE`].apply(new Controller(req,rep),[{},500,Config.APP_DEBUG?error.stack:"SystemError"]))
        }catch (e){
            console.log(e);
        }
    }

}
exports.Server = Server;